package in.smartbillpay.web.controllers;

import in.smartbillpay.web.models.Authority;
import in.smartbillpay.web.models.City;
import in.smartbillpay.web.models.District;
import in.smartbillpay.web.models.State;
import in.smartbillpay.web.services.AuthorityService;
import in.smartbillpay.web.services.LocationService;
import in.smartbillpay.web.validators.DistrictValidator;
import in.smartbillpay.web.validators.FormPostResponse;
import in.smartbillpay.web.validators.StateValidator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 * Handles requests for the application home page.
 */
@Controller
@RequestMapping("/secure/admin")
public class AdminController {
	
	private static final Logger logger = LoggerFactory.getLogger(AdminController.class);
	
	@Autowired
	private AuthorityService authorityService;
	
	@Autowired
	private LocationService locationService;
	
	@Autowired
	private StateValidator stateValidator;
	
	@Autowired
	private DistrictValidator districtValidator;
	
	/**
	 * @return the logger
	 */
	public static Logger getLogger() {
		return logger;
	}

	/**
	 * Simply selects the admin home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String getAdminConsole() {
		logger.info("Inside getAdminConsole()");
		return "admin";
	}
	
	/**
	 * Render the Authority module
	 * 1. Display the list of exiting authorities
	 * 2. Provide the form to Add new Authority
	 */
	@RequestMapping(value = "/authority", method = RequestMethod.GET)
	public ModelAndView getAuthorities() {
		logger.info("Inside getAuthorities()");		
		List<Authority> authorityList = authorityService.getAllAuhtorities();
		logger.info("authorityList count is- " + authorityList.size() );
		ModelAndView mav = new ModelAndView("authority");
		mav.addObject("authorityList", authorityList);
		// Command object for adding new form
		Authority authority = new Authority();
		mav.addObject("authority", authority);
		return  mav;		
	}

	@RequestMapping(value = "/authority/add", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse addAuthority(@Valid Authority authority,BindingResult result) {
		logger.info("Inside addAuthority()");
		FormPostResponse response = new FormPostResponse();
		
		if(result.hasErrors()){
			Map<String,String> errorMap = new HashMap<String,String>();
			response.setErrorMap(errorMap);
			for(FieldError error:result.getFieldErrors() ){
				
				errorMap.put(error.getField(), error.getDefaultMessage());
				logger.info(error.getField() + " " + error.getDefaultMessage());
			}
				
			response.setStatus("error");
			logger.info(Integer.toString(result.getErrorCount()));
		}
		else{
			Authority savedAuthority = authorityService.save(authority);
			logger.info(Integer.toString(savedAuthority.getId()));
			Map<String,String> resultMap = new HashMap<String,String>();
			resultMap.put("newAuthorityId", Integer.toString(savedAuthority.getId()));
			resultMap.put("newAuthorityName", savedAuthority.getName());
			response.setStatus("Success");
			response.setResultMap(resultMap);			
		}		
		return  response;
	}

	@RequestMapping(value = "/authority/delete", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse deleteAuthority(@RequestParam("authorityId") int authorityId) {
		logger.info("Inside deleteAuthority()");
		FormPostResponse response = new FormPostResponse();
					
		authorityService.delete(authorityService.getAuthority(authorityId));
		response.setStatus("Success");
				
		return  response;
	}
	
	@RequestMapping(value = "/state", method = RequestMethod.GET)
	public ModelAndView getState() {
		logger.info("Inside getState()");
		List<State> stateList = locationService.getAllState();
		logger.info("Statelist count is- " + stateList.size() );
		ModelAndView mav = new ModelAndView("state");
		mav.addObject("stateList", stateList);
		State state = new State();
		mav.addObject("state", state);
		return  mav;
	}
	
	@RequestMapping(value = "/state/add", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse addState(@Valid State state,BindingResult result) {
		logger.info("Inside addState()");
		FormPostResponse response = new FormPostResponse();
		stateValidator.validate(state, result);
		if(result.hasErrors()){
			Map<String,String> errorMap = new HashMap<String,String>();
			response.setErrorMap(errorMap);
			for(FieldError error:result.getFieldErrors() ){
				
				errorMap.put(error.getField(), error.getDefaultMessage());
				logger.info(error.getField() + " " + error.getDefaultMessage());
			}
				
			response.setStatus("error");
			logger.info(Integer.toString(result.getErrorCount()));
		}
		else{
			State savedState = locationService.saveOrUpdate(state);
			logger.info(Integer.toString(savedState.getId()));
			Map<String,String> resultMap = new HashMap<String,String>();
			resultMap.put("newStateId", Integer.toString(savedState.getId()));
			resultMap.put("newStateName", savedState.getName());
			response.setStatus("Success");
			response.setResultMap(resultMap);			
		}		
		return  response;
	}
	
	@RequestMapping(value = "/state/delete", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse deleteState(@RequestParam("stateId") int stateId) {
		logger.info("Inside deleteState()");
		FormPostResponse response = new FormPostResponse();
		
		List<District> districtList = locationService.getDistricts(stateId);
		logger.info("District count - " + districtList.size());
		
		if(districtList.size() > 0){
			response.setStatus("Error");
			Map<String,String> errorMap = new HashMap<String,String>();
			errorMap.put("errorMessage", "At least one district has a reference of this state. Please delete those district records " +
					"before deleting this state");
			response.setErrorMap(errorMap);
		}
		else {
			locationService.deleteState(stateId);
			response.setStatus("Success");
		}
		
		return  response;
	}
	
	@RequestMapping(value = "/district", method = RequestMethod.GET)
	public ModelAndView getDistrict() {
		logger.info("Inside getDistrict()");
		ModelAndView mav = new ModelAndView("district");
		List<State> stateList = locationService.getAllState();
		mav.addObject("stateList", stateList);
		String viewType = "districtSearch";
		mav.addObject("viewType", viewType);
		return mav;
	}
	
	@RequestMapping(value = "/district/state/{stateId}", method = RequestMethod.GET)
	public ModelAndView getDistrictList(@PathVariable int stateId, HttpSession session) {
		logger.info("Inside getDistrictList()");
		logger.info("stateId is -" + Integer.toString(stateId));
		ModelAndView mav = new ModelAndView("district_list");
		session.setAttribute("stateId", Integer.toString(stateId));
		State state =locationService.getStateById(stateId);
		session.setAttribute("stateName", state.getName());
		
		List<District> districtList = locationService.getDistricts(stateId);
		logger.info("count of records -" + Integer.toString(districtList.size()));
		mav.addObject("districtList", districtList);
		String viewType = "districtList";
		mav.addObject("viewType", viewType);
		// Command object for form
		District district = new District();
		mav.addObject("district", district);
		return mav;
	}
	
	@RequestMapping(value = "/district/add", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse addDistrict(@Valid District district,BindingResult result,HttpSession session) {
		logger.info("Inside addState()");
		logger.info("StateId from session is -" + session.getAttribute("stateId") );
				FormPostResponse response = new FormPostResponse();
		districtValidator.validate(district, result);
		if(result.hasErrors()){
			Map<String,String> errorMap = new HashMap<String,String>();
			response.setErrorMap(errorMap);
			for(FieldError error:result.getFieldErrors() ){
				
				errorMap.put("errorMessage", error.getDefaultMessage());
				logger.info(error.getField() + " " + error.getDefaultMessage());
			}
				
			response.setStatus("error");
			logger.info(Integer.toString(result.getErrorCount()));
		}
		else{
			String stateString = (String)session.getAttribute("stateId");
			int id =Integer.parseInt(stateString);
			State state= locationService.getStateById(id);
			district.setState(state);
			District savedDistrict = locationService.saveOrUpdate(district);

			logger.info(Integer.toString(district.getId()));
			Map<String,String> resultMap = new HashMap<String,String>();
			resultMap.put("newDistrictId", Integer.toString(savedDistrict.getId()));
			resultMap.put("newDistrictName", savedDistrict.getName());
			response.setStatus("Success");
			response.setResultMap(resultMap);
			
		}
		
		return  response;
	}
	
	@RequestMapping(value = "/district/delete", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse deleteDistrict(@RequestParam("districtId") int districtId) {
		logger.info("Inside deleteDistrict()");
		FormPostResponse response = new FormPostResponse();
	
		List<City> cityList = locationService.getCities(districtId);
		
		logger.info("district size is -" + cityList.size());
		
		if(cityList.size() > 0){
			response.setStatus("Error");
			Map<String,String> errorMap = new HashMap<String,String>();
			errorMap.put("errorMessage", "At least one city has a reference of this district. Please delete those city records " +
					"before deleting this district");
			response.setErrorMap(errorMap);
		}
		else {
			locationService.deleteDistrict(districtId);
			response.setStatus("Success");
		}
				
		return  response;
	}
	
	@RequestMapping(value = "/city", method = RequestMethod.GET)
	public ModelAndView getCity() {
		logger.info("Inside getCity()");
		ModelAndView mav = new ModelAndView("city");
		List<State> stateList = locationService.getAllState();
		mav.addObject("stateList", stateList);
		int defaultStateId = stateList.get(0).getId();
		for (State state : stateList) {
			if(state.getName().equalsIgnoreCase("Maharashtra")){
				defaultStateId = state.getId();
				break;
			}
		}
		mav.addObject("defaultStateId", defaultStateId);
		List<District> districtList = locationService.getDistricts(defaultStateId);
		mav.addObject("districtList", districtList);
		return mav;
	}
	
	@RequestMapping(value = "/city/district/{districtId}", method = RequestMethod.GET)
	public ModelAndView getCityList(@PathVariable int districtId, HttpSession session) {
		logger.info("Inside getCityList()");
		logger.info("districtId is -" + Integer.toString(districtId));
		
		ModelAndView mav = new ModelAndView("city_list");
		session.setAttribute("districtId", Integer.toString(districtId));
		
		District district = locationService.getDistrictById(districtId);
		
		List<City> cityList = locationService.getCities(districtId);
		logger.info("count of records -" + Integer.toString(cityList.size()));
		logger.info("District -" + district.getName() );
		mav.addObject("cityList", cityList);
		mav.addObject("district", district);
		// Command object for form
		City city = new City();
		mav.addObject("city", city);
		return mav;
	}
	
	@RequestMapping(value = "/city/add", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse addCity(@Valid City city,BindingResult result,HttpSession session) {
		logger.info("Inside addCity()");
		logger.info("districtId from session is -" + session.getAttribute("districtId") );
				FormPostResponse response = new FormPostResponse();
	//	districtValidator.validate(district, result);
		if(result.hasErrors()){
			Map<String,String> errorMap = new HashMap<String,String>();
			response.setErrorMap(errorMap);
			for(FieldError error:result.getFieldErrors() ){
				
				errorMap.put("errorMessage", error.getDefaultMessage());
				logger.info(error.getField() + " " + error.getDefaultMessage());
			}
				
			response.setStatus("error");
			logger.info(Integer.toString(result.getErrorCount()));
		}
		else{
			String districtString = (String)session.getAttribute("districtId");
			int id =Integer.parseInt(districtString);
			District district= locationService.getDistrictById(id);
			city.setDistrict(district);
			City savedCity = locationService.saveOrUpdate(city);

			logger.info(Integer.toString(district.getId()));
			Map<String,String> resultMap = new HashMap<String,String>();
			resultMap.put("newCityId", Integer.toString(savedCity.getId()));
			resultMap.put("newCityName", savedCity.getName());
			response.setStatus("Success");
			response.setResultMap(resultMap);
			
		}
		
		return  response;
	}
	
	@RequestMapping(value = "/city/delete", method = RequestMethod.POST)
	public @ResponseBody FormPostResponse deleteCity(@RequestParam("cityId") int cityId) {
		logger.info("Inside deleteCity()");
		FormPostResponse response = new FormPostResponse();
		locationService.deleteCity(cityId);
		response.setStatus("Success");
		return  response;
	}
	
	public AuthorityService getAuthorityService() {
		return authorityService;
	}

	public void setAuthorityService(AuthorityService authorityService) {
		this.authorityService = authorityService;
	}

	/**
	 * @return the locationService
	 */
	public LocationService getLocationService() {
		return locationService;
	}

	/**
	 * @param locationService the locationService to set
	 */
	public void setLocationService(LocationService locationService) {
		this.locationService = locationService;
	}

	/**
	 * @return the stateValidator
	 */
	public StateValidator getStateValidator() {
		return stateValidator;
	}

	/**
	 * @param stateValidator the stateValidator to set
	 */
	public void setStateValidator(StateValidator stateValidator) {
		this.stateValidator = stateValidator;
	}

	/**
	 * @return the districtValidator
	 */
	public DistrictValidator getDistrictValidator() {
		return districtValidator;
	}

	/**
	 * @param districtValidator the districtValidator to set
	 */
	public void setDistrictValidator(DistrictValidator districtValidator) {
		this.districtValidator = districtValidator;
	}
	
	
}
